package reliability

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/sirupsen/logrus"

	"xagent/internal/monitoring"
)

// State represents the circuit breaker state
type State string

const (
	StateClosed   State = "closed"   // Circuit is closed and operating normally
	StateOpen     State = "open"     // Circuit is open and rejecting requests
	StateHalfOpen State = "halfOpen" // Circuit is allowing a limited number of requests
)

// Config represents circuit breaker configuration
type Config struct {
	Name                 string
	MaxFailures          int           // Maximum number of failures before opening
	Timeout              time.Duration // How long to keep circuit open
	HalfOpenMaxRequests  int           // Maximum requests in half-open state
	FailureThresholdRate float64       // Failure rate threshold (0-1)
	MinimumRequests      int           // Minimum requests before calculating failure rate
	SuccessThresholdRate float64       // Success rate threshold to close circuit (0-1)
}

// CircuitBreaker implements the circuit breaker pattern
type CircuitBreaker struct {
	config     *Config
	state      State
	failures   int
	requests   int
	successes  int
	lastChange time.Time
	mutex      sync.RWMutex
	logger     *logrus.Logger
	metrics    *monitoring.MetricsRegistry
}

// NewCircuitBreaker creates a new circuit breaker
func NewCircuitBreaker(config *Config, logger *logrus.Logger) *CircuitBreaker {
	if config.MaxFailures == 0 {
		config.MaxFailures = 5
	}
	if config.Timeout == 0 {
		config.Timeout = 60 * time.Second
	}
	if config.HalfOpenMaxRequests == 0 {
		config.HalfOpenMaxRequests = 3
	}
	if config.FailureThresholdRate == 0 {
		config.FailureThresholdRate = 0.5
	}
	if config.MinimumRequests == 0 {
		config.MinimumRequests = 10
	}
	if config.SuccessThresholdRate == 0 {
		config.SuccessThresholdRate = 0.8
	}

	return &CircuitBreaker{
		config:     config,
		state:      StateClosed,
		lastChange: time.Now(),
		logger:     logger,
		metrics:    monitoring.GetMetricsRegistry(),
	}
}

// Execute executes a function with circuit breaker protection
func (cb *CircuitBreaker) Execute(ctx context.Context, operation func() error) error {
	if !cb.allowRequest() {
		return fmt.Errorf("circuit breaker '%s' is open", cb.config.Name)
	}

	startTime := time.Now()
	err := operation()
	duration := time.Since(startTime)

	cb.recordResult(err, duration)
	return err
}

// allowRequest checks if a request should be allowed
func (cb *CircuitBreaker) allowRequest() bool {
	cb.mutex.RLock()
	defer cb.mutex.RUnlock()

	switch cb.state {
	case StateClosed:
		return true
	case StateOpen:
		if time.Since(cb.lastChange) > cb.config.Timeout {
			cb.transitionTo(StateHalfOpen)
			return true
		}
		return false
	case StateHalfOpen:
		return cb.requests < cb.config.HalfOpenMaxRequests
	default:
		return false
	}
}

// recordResult records the result of an operation
func (cb *CircuitBreaker) recordResult(err error, duration time.Duration) {
	cb.mutex.Lock()
	defer cb.mutex.Unlock()

	cb.requests++
	if err != nil {
		cb.failures++
		cb.metrics.TaskErrors.WithLabelValues(
			cb.config.Name,
			"circuit_breaker",
			err.Error(),
		).Inc()
	} else {
		cb.successes++
	}

	cb.metrics.TaskDuration.WithLabelValues(
		cb.config.Name,
		"circuit_breaker",
	).Observe(duration.Seconds())

	switch cb.state {
	case StateClosed:
		if cb.shouldOpen() {
			cb.transitionTo(StateOpen)
		}
	case StateHalfOpen:
		if cb.shouldClose() {
			cb.transitionTo(StateClosed)
		} else if cb.shouldOpen() {
			cb.transitionTo(StateOpen)
		}
	}
}

// shouldOpen checks if the circuit should open
func (cb *CircuitBreaker) shouldOpen() bool {
	if cb.requests < cb.config.MinimumRequests {
		return false
	}

	failureRate := float64(cb.failures) / float64(cb.requests)
	return failureRate >= cb.config.FailureThresholdRate
}

// shouldClose checks if the circuit should close
func (cb *CircuitBreaker) shouldClose() bool {
	if cb.requests < cb.config.HalfOpenMaxRequests {
		return false
	}

	successRate := float64(cb.successes) / float64(cb.requests)
	return successRate >= cb.config.SuccessThresholdRate
}

// transitionTo changes the circuit breaker state
func (cb *CircuitBreaker) transitionTo(newState State) {
	oldState := cb.state
	cb.state = newState
	cb.lastChange = time.Now()
	cb.requests = 0
	cb.failures = 0
	cb.successes = 0

	cb.logger.WithFields(logrus.Fields{
		"circuit_breaker": cb.config.Name,
		"old_state":       oldState,
		"new_state":       newState,
	}).Info("Circuit breaker state changed")

	cb.metrics.AgentStatus.WithLabelValues(
		cb.config.Name,
		string(newState),
	).Set(1)
	cb.metrics.AgentStatus.WithLabelValues(
		cb.config.Name,
		string(oldState),
	).Set(0)
}

// GetState returns the current state
func (cb *CircuitBreaker) GetState() State {
	cb.mutex.RLock()
	defer cb.mutex.RUnlock()
	return cb.state
}

// Reset resets the circuit breaker to its initial state
func (cb *CircuitBreaker) Reset() {
	cb.mutex.Lock()
	defer cb.mutex.Unlock()

	cb.state = StateClosed
	cb.lastChange = time.Now()
	cb.requests = 0
	cb.failures = 0
	cb.successes = 0

	cb.logger.WithField("circuit_breaker", cb.config.Name).Info("Circuit breaker reset")
}
